let { Matrix4, Object3D } = THREE;

class Object2DR extends Object3D {
  constructor(el) {
    super();
    this.element = el || document.createElement("div");
    this.element.style.position = "absolute";
    this.addEventListener("removed", () => {
      this.traverse((object) => {
        if (object.element instanceof Element && object.element) {
          object.element.parentNode.removeChild(object.element);
        }
      });
    });
  }
  copy(source, recursive) {
    super.copy(source, recursive);
    this.element = source.element.cloneNode(true);
    return this;
  }
}

Object2DR.prototype.isObject2DR = true;

const _vector = new Vector3();
const _viewMatrix = new Matrix4();
const _viewProjectionMatrix = new Matrix4();
const _a = new Vector3();
const _b = new Vector3();

class CSS2DRenderer {
  constructor() {
    const _this = this;

    let _width, _height;
    let _widthHalf, _heightHalf;

    const cache = {
      objects: new WeakMap(),
    };

    const domElement = document.createElement("div");
    domElement.style.overflow = "hidden";
    this.domElement = domElement;

    this.setSize = (width, height) => {
      _width = width;
      _height = height;

      _widthHalf = width / 2;
      _heightHalf = height / 2;

      domElement.style.width = width + "px";
      domElement.style.height = height + "px";
    };
    this.getSize = () => {
      return {
        width: _width,
        height: _height,
      };
    };

    this.render = (scene, camera) => {
      if (scene.matrixAutoUpdate === true) scene.updateMatrixWorld();
      if (camera.parent === null) camera.updateMatrixWorld();

      // 获得逆矩阵 && 投影变换矩阵
      // 坐标系变换
      _viewMatrix.copy(camera.matrixWorldInverse);
      _viewProjectionMatrix.multiplyMatrices(
        camera.projectionMatrix,
        _viewMatrix
      );

      renderObject(scene, scene, camera);
      zOder(scene);
    };

    const renderObject = (object, scene, camera) => {
      if (object.isObject2DR) {
        // 渲染前的预处理
        object.onBeforeRender(_this, scene, camera);
        // 返回从矩阵中的元素得到的新向量值的向量
        // setFromMatrixPosition: function ( m ) {

        //     this.x = m.elements[ 12 ];
        //     this.y = m.elements[ 13 ];
        //     this.z = m.elements[ 14 ];

        //     return this;	//返回三维向量
        _vector.setFromMatrixPosition(object.matrixWorld);
        _vector.applyMatrix4(_viewProjectionMatrix);
        const element = object.element;
        // 兼容性考虑
        if (/apple/i.test(navigator.vendor)) {
          element.style.transform = `translate(-50%,-50%) translate(${Math.round(
            _vector.x * _widthHalf + _widthHalf
          )}px,${Math.round(-_vector.y * _heightHalf + _heightHalf)}px )`;
        } else {
          element.style.transform = `translate(-50%,-50%) translate(${
            _vector.x * _widthHalf + _widthHalf
          }px,${-_vector.y * _heightHalf + _heightHalf}px )`;
        }

        element.style.display =
          object.visible && _vector.z >= -1 && _vector.z <= 1 ? "" : "none";
        const objectData = {
          distanceToCameraSquared: getDistanceToSquared(camera, object),
        };
        cache.objects.set(object, objectData);
        if (element.parentNode != domElement) {
          domElement.appendChild(element);
        }
        // 渲染后的收尾
        object.onAfterRender(_this, scene, camera);
      }
      // 循环遍历渲染所有子对象
      for (let i = 0, l = object.children.length; i < l; i++) {
        renderObject(object.children[i], scene, camera);
      }
    };

    const getDistanceToSquared = (object1, object2) => {
      // 输入矩阵返回三维向量
      _a.setFromMatrixPosition(object1.matrixWorld);
      _b.setFromMatrixPosition(object2.matrixWorld);
      return _a.distanceToSquared(_b);
    };

    const filterAndFlatten = (scene) => {
      const result = [];
      scene.traverse((object) => {
        if (object.isObject2DR) result.push(object);
      });
      return result;
    };

    const zOder = (scene) => {
      const sorted = filterAndFlatten(scene).sort((a, b) => {
        const distanceA = cache.objects.get(a).distanceToCameraSquared;
        const distanceB = cache.objects.get(b).distanceToCameraSquared;
        return distanceA - distanceB;
      });
      // （到相机的距离）自小到大排序
      const zMax = sorted.length;
      // 设置视图的层次
      for (let i = 0, l = sorted.length; i < l; i++) {
        sorted[i].element.style.zIndex = zMax - i;
      }
    };
  }
}
